use axum::{
    Json,
    http::Request,
    response::Response,
    middleware::Next, body::Body
};
use serde_json::Value;
use redis::{Commands, RedisError};
use dotenv::dotenv;
use std::env;
use crate::{ code, query, rdb, uuid};

use super::{claims::get_req_claims, jwt::{token_encode, Claims}};

// 生成 token 字符串，并将登录状态存入 redis
pub fn create_token_to_redis(data:Option<Value> ) -> Result<Json<Value>, Json<Value>>{
    if data.is_none() { 
        return code::fail(203)
    }
    dotenv().ok();
    let env_expire = env::var("EXPIRE").unwrap_or_default();
    let expire = if env_expire.is_empty() { config::EXPIRE as i64 } else { env_expire.parse::<i64>().unwrap_or(0) };
    let f = &mut data.unwrap();
    let uuid = uuid::create();
    let tok = token_encode(
        query::i32(f, "id")?, 
        query::i32_op(f, "cid")?, 
        query::i32_op(f, "role")?, 
        query::str(f, "ident")?, 
        query::str_op(f, "casc")?, 
        query::str(f, "veri")?,
        Some(uuid.to_owned())
    )?;
    let mut conn = rdb::connect("0")?;
    let _:() = conn.set(&uuid, &tok).unwrap();
    let _:() = conn.expire(&uuid, expire).unwrap();
    f["token"] = tok.into();
    code::data(f.to_owned())
}
// 验证接口登录状态的中间件
pub async fn auth(req: Request<Body>, next: Next) -> Result<Response, Json<Value>>{
    get_req_claims(req, next, false,|d|auth_token(d)).await
}
// 登录验证 token 是否过期，并在一定时间内自动续签 token
pub fn auth_token(claims: Claims) -> Result<(), Json<Value>> {
    let key = &claims.uuid;
    let mut conn = rdb::connect("0")?;
    let k: Result<String, RedisError> = conn.get(&key);
    match k {
        Ok(_f) => {
            dotenv().ok();
            let env_expire = env::var("EXPIRE").unwrap_or_default();
            let expire = if env_expire.is_empty() { config::EXPIRE as i64 } else { env_expire.parse::<i64>().unwrap_or(0) };

            let exp:i32 = conn.ttl(&key).unwrap();           // 续签 token 过期时间
            if exp > 0 && exp < (expire as i32)/5{
                let _:() = conn.expire(&key, expire).unwrap();
            }
            Ok(())
        },
        _ => Err(code::fail_(202))
    }
}